package com.chat.common.user.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.chat.common.chat.domain.entity.RoomFriend;
import com.chat.common.chat.service.ChatService;
import com.chat.common.chat.service.adapter.MessageAdapter;
import com.chat.common.common.annotation.RedissonLock;
import com.chat.common.common.domain.vo.req.CursorPageBaseReq;
import com.chat.common.common.domain.vo.req.PageBaseReq;
import com.chat.common.common.domain.vo.resp.CursorPageBaseResp;
import com.chat.common.common.domain.vo.resp.PageBaseResp;
import com.chat.common.common.event.UserApplyEvent;
import com.chat.common.common.utils.AssertUtil;
import com.chat.common.user.dao.UserApplyDao;
import com.chat.common.user.dao.UserDao;
import com.chat.common.user.dao.UserFriendDao;
import com.chat.common.user.domain.entity.User;
import com.chat.common.user.domain.entity.UserApply;
import com.chat.common.user.domain.entity.UserFriend;
import com.chat.common.user.domain.enums.ApplyStatusEnum;
import com.chat.common.user.domain.vo.req.friend.FriendApplyReq;
import com.chat.common.user.domain.vo.req.friend.FriendApproveReq;
import com.chat.common.user.domain.vo.req.friend.FriendCheckReq;
import com.chat.common.user.domain.vo.resp.friend.FriendApplyResp;
import com.chat.common.user.domain.vo.resp.friend.FriendCheckResp;
import com.chat.common.user.domain.vo.resp.friend.FriendResp;
import com.chat.common.user.domain.vo.resp.friend.FriendUnreadResp;
import com.chat.common.chat.service.IRoomService;
import com.chat.common.user.service.IUserFriendService;
import com.chat.common.user.service.adapter.FriendAdapter;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import static com.chat.common.user.domain.enums.ApplyStatusEnum.WAIT_APPROVAL;

@Service
@Slf4j
public class FriendServiceImpl implements IUserFriendService {

    @Resource
    private UserFriendDao FriendDao;
    @Resource
    private UserDao userDao;
    @Resource
    private ApplicationEventPublisher applicationEventPublisher;
    @Resource
    private UserApplyDao applyDao;
    @Resource
    private UserApplyDao userApplyDao;
    @Resource
    private UserFriendDao userFriendDao;
    @Resource
    private IRoomService roomService;
    @Resource
    private ChatService chatService;

    @Override
    public CursorPageBaseResp<FriendResp> friendList(Long uid, CursorPageBaseReq req) {
        //根据自己的id 查询出自己的好友
        CursorPageBaseResp<UserFriend> friendPage = FriendDao.getFriendPage(uid, req);
        if (CollectionUtils.isEmpty(friendPage.getList())) {
            return CursorPageBaseResp.empty();
        }
        //查询出全部的好友
        List<Long> friendUids = friendPage.getList()
                .stream().map(UserFriend::getFriendUid)
                .collect(Collectors.toList());
        //根据好友的id 查询好友的信息(在线状态)
        List<User> userList = userDao.getFriendList(friendUids);
        return CursorPageBaseResp.init(friendPage, FriendAdapter.buildFriend(friendPage.getList(), userList));
    }

    /**
     * 批量判断是否是自己的好友
     * @param uid
     * @param request
     * @return
     */
    @Override
    public FriendCheckResp check(Long uid, FriendCheckReq request) {
        //根据自己的uid 匹配是否是自己的好友
        List<UserFriend> friendList = FriendDao.getByFriends(uid, request.getUidList());
        //获取全部的friendId
        Set<Long> collect = friendList.stream().map(UserFriend::getFriendUid).collect(Collectors.toSet());
        List<FriendCheckResp.FriendCheck> list = request.getUidList().stream().map(friendUid -> {
            FriendCheckResp.FriendCheck friendCheck = new FriendCheckResp.FriendCheck();
            friendCheck.setUid(friendUid);
            friendCheck.setFriend(collect.contains(friendUid));
            return friendCheck;
        }).collect(Collectors.toList());

        return new FriendCheckResp(list);
    }

    /**
     * 申请好友
     * @param uid
     * @param request
     */
    @RedissonLock(key = "#uid")
    @Override
    public void apply(Long uid, FriendApplyReq request) {
        //先检查 targetUid 二者是否为好友
        UserFriend friend = FriendDao.getByFriend(uid, request.getTargetUid());
        AssertUtil.isEmpty(friend,"你们已经是好友了");
        //是否已经有添加的记录 但是对方还没通过
        UserApply selfApproving = applyDao.getFriendApproving(uid, request.getTargetUid());
        if(Objects.nonNull(selfApproving)){
            log.error("用户：{}，申请用户:{}为好友 记录已经存在",uid,request.getTargetUid());
            return;
        }
        //已有添加记录 但是自己未通过 无需继续发送申请 点击接受即可
        UserApply friendApproving = applyDao.getFriendApproving(request.getTargetUid(),uid );
        if(Objects.nonNull(friendApproving)){
            //通过代理调用同类下的 同意方法 将 apply的主键和点击申请的用户的uid 传入
            ((IUserFriendService)AopContext.currentProxy()).applyApprove(uid,new FriendApproveReq(friendApproving.getId()));
        }
        // 发送申请
        UserApply userApply = FriendAdapter.buildApplyFriend(uid, request);
        applyDao.save(userApply);
        //接收人的未读消息数量 推送
        applicationEventPublisher.publishEvent(new UserApplyEvent(this,userApply));
    }

    //审批好友申请通过
    @Override
    @Transactional(rollbackFor = Exception.class)
    @RedissonLock(key = "#uid")
    public void applyApprove(Long uid, FriendApproveReq request) {
        UserApply userApply = userApplyDao.getById(request.getApplyId());
        AssertUtil.isNotEmpty(userApply, "不存在申请记录");
        AssertUtil.equal(userApply.getTargetId(), uid, "不存在申请记录");
        AssertUtil.equal(userApply.getStatus(), WAIT_APPROVAL.getCode(), "已同意好友申请");
        //同意申请
        userApplyDao.agree(request.getApplyId());
        //创建双方好友关系
        createFriend(uid, userApply.getUid());
        //创建一个聊天房间
        RoomFriend roomFriend = roomService.createFriendRoom(Arrays.asList(uid, userApply.getUid()));
        //发送一条同意消息。。我们已经是好友了，开始聊天吧
        chatService.sendMsg(MessageAdapter.buildAgreeMsg(roomFriend.getRoomId()), uid);
    }

    private void createFriend(Long uid, Long targetUid) {
        UserFriend userFriend1 = new UserFriend();
        userFriend1.setUid(uid);
        userFriend1.setFriendUid(targetUid);
        UserFriend userFriend2 = new UserFriend();
        userFriend2.setUid(targetUid);
        userFriend2.setFriendUid(uid);
        userFriendDao.saveBatch(Lists.newArrayList(userFriend1, userFriend2));
    }

    //用户的消息未读数
    @Override
    public FriendUnreadResp unread(Long uid) {
        Integer unread = userApplyDao.unread(uid);
        return new FriendUnreadResp(unread);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteFriend(Long uid, Long friendUid) {
        //删除两个人的好友关系 两个人的room
        List<UserFriend> friendList = userFriendDao.getUserFriend(uid, friendUid);
        if(CollectionUtils.isEmpty(friendList)){
            log.info("没有好友关系：{},{}", uid, friendUid);
            return;
        }
        //存在好友关系 将好友关系更改为 已删除
        Set<Long> ids = friendList.stream().map(UserFriend::getId).collect(Collectors.toSet());
        userFriendDao.removeByIds(ids);
        //禁用房间
        roomService.disableFriendRoom(Arrays.asList(uid,friendUid));
    }

    //好友申请列表
    @Override
    public PageBaseResp<FriendApplyResp> pageApplyFriend(Long targetId, PageBaseReq request) {
        IPage<UserApply>  page = userApplyDao.friendApplyPage(targetId, request.plusPage());
        if(CollectionUtils.isEmpty(page.getRecords())){
            return PageBaseResp.empty();
        }
        //将这些申请列表设为已读
        readApples(targetId, page);
        //返回消息
        return PageBaseResp.init(page, FriendAdapter.buildFriendApplyList(page.getRecords()));
    }
    //将这些申请列表设为已读
    private void readApples(Long targetId, IPage<UserApply> page) {
        Set<Long> collect = page.getRecords().stream()
                .map(UserApply::getId)
                .collect(Collectors.toSet());
        userApplyDao.readApples(targetId, collect);

    }


}
